package com.googlecode.cswish.struts;

import java.io.File;
import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.PrepareOperations;

import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.security.SafeManager;
import com.googlecode.cswish.security.auth.UserInfo;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.FrameConstant;

public class StrutsExecuteFilter extends org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter {
	
	private final static Logger PT = Logger.getLogger("PT");
	
	private final static Logger logger = Logger.getLogger(StrutsExecuteFilter.class);
	
	private SafeManager safeManager;
	
	private SafeManager getSafeManager() {
		if (safeManager == null) {
			safeManager = ObjectFactory.getInstance().getBean(SafeManager.class, false);
		}
		return safeManager;
	}
	
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        // This is necessary since we need the dispatcher instance, which was created by the prepare filter
		if (execute == null) {
			lazyInit();
		}

        ///////////////////////// ADDITION CODE //////////////////////////////////
		String uri = Routing.getUri(request);
        //////////////////////////////////////////////////////////////////////////
        
        //if recusrion counter is > 1, it means we are in a "forward", in that case a mapping will still be
        //in the request, if we handle it, it will lead to an infinte loop, see WW-3077
        Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);
        ActionMapping mapping = prepare.findActionMapping(request, response);
        if (mapping == null) {
        	// try to fix issue: DefaultActionMapper.getUri
        	String curUri = (String) request.getAttribute("javax.servlet.include.servlet_path");
        	if (curUri != null) {
        		int index =curUri.indexOf('?');
        		if (index > 0) {
        			// find the mapping again
        			request.setAttribute("javax.servlet.include.servlet_path", curUri.substring(0, index));
        			mapping = prepare.findActionMapping(request, response);
        			request.setAttribute("javax.servlet.include.servlet_path", curUri);
        		}
        	}
        }
        if (mapping == null || uri.startsWith("/static/") || recursionCounter > 1) {
        	if (uri.endsWith(".jsp")) {
        		// TODO: can load it from the class path?
        		// TODO: forward to site directory?
        		String path = super.filterConfig.getServletContext().getRealPath(uri);
        		if (new File(path).exists()) {
        			request.getRequestDispatcher(uri).forward(request, response);
        		} else {
        			response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid Request");
        		}
        	} else {
        		// TODO: disable this function? see TagUtil.getCssPath / TagUtil.getScriptPath / TagUtil.getImagePath
	            boolean handled = execute.executeStaticResourceRequest(request, response);
	            if (handled) {
	            	// continue to check the resource flag
	            	handled = request.getAttribute(FrameConstant.STATIC_NOT_FOUND) == null;
	            }
	            if (!handled ) {
	                chain.doFilter(request, response);
	            }
        	}
        } else {
        	boolean enableDebug = PT.isDebugEnabled();
        	long t1 = 0;
        	if (enableDebug) {
        		t1 = System.currentTimeMillis();
        	}
        	
        	try {
        		execute.executeAction(request, response, mapping);
        		
        	} catch (ApplicationException ex) {
        		response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid Request");
        	} catch (Exception ex) {
        		logger.error("Visit page: " + Routing.getUri(request), ex);
        		// TODO: use generic.error.ftl to show the error? see Dispatcher.sendError
        		response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid Request");
        	}
        	
            if (enableDebug) {
            	long t2 = System.currentTimeMillis();
            	long time = t2 - t1;
            	UserInfo userInfo = getSafeManager().getLoginUser(request);
            	String userId = userInfo == null ? "anony" : userInfo.getLoginId();
            	String ip = getRemoteIp(request);
            	
            	if (time < 500) {
            		PT.debug(userId + " 【" + ip + "】 " + uri + " spent " + time);
            	} else {
            		PT.debug("LP! " + userId + " 【" + ip + "】 " + uri + " spent " + time);
            	}
            	
            	getSafeManager().notifyUserVisit(userId, ip, uri, t1, time);
            }
        }
    }
	
	private String getRemoteIp(HttpServletRequest request) {
		String ip = request.getHeader("X-Real-IP");
		if (ip == null || ip.length() == 0) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
}
